# coding=utf-8

##############################################################################
#
# utilities.py
# A utility module
#
# Copyright (c) 2007 Webcore Corp. All Rights Reserved.
#
##############################################################################
""" utilities.py - A base class to provide basic plugin architecture

$Id: utilities.py 19 2011-09-28 07:33:04Z shib.ats $
"""

__author__  = 'Atsushi Shibata <shibata@webcore.co.jp>'
__docformat__ = 'plaintext'
__licence__ = 'MIT'

import imp
import os
import logging
from traceback import print_exc
from StringIO import StringIO
from datetime import datetime
from urllib2 import HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm,\
                    Request, build_opener, urlopen

log = logging.getLogger("baseplugin")

def exp2log(logobj=logging.debug):
    """
    Exceptionを文字列に変換してlogにdebug用に出力する
    """
    s = StringIO()
    print_exc(file=s)
    s.seek(0)
    logobj('\n'+s.read())

def seek_files(dirname, basepath='', matchmethod=None):
    """
    パスを指定してファイルをスキャン，プラグインを探す
    
    dirname     : ファイルを探すのに使うディレクトリ名
    basepath    : スキャンするベースとなるパス
    matchmethod : プラグインの判定を行うmethodへのフック
                  パスを引数として受け取り，True, Falseを返す
    TODO:recursiveオプション
    """

    def match_pymodule(base, fdn):
        """
        Pythonのモジュール/パッケージにmatchするmatch関数，デフォルトとして利用される
        """
        if fdn == 'tests':
            # test用ディレクトリは無視
            return False
        if fdn.startswith('.') or fdn.startswith('__init__.py'):
            # ドットで始まるファイルは無視
            return False
        if [x for x in ['.py', '.pyc'] if fdn.endswith(x)]:
            # モジュールファイル
            tmp_fdn = fdn.replace('.pyc', '')
            return tmp_fdn.replace('.py', '')
        if os.path.isdir(os.path.join(base, fdn)):
            # ディレクトリであれば__init__.py*があるかどうか調べる
            if [x for x in ['.py', '.pyc']
                if os.path.exists(os.path.join(base, fdn, '__init__'+x))]:
                return fdn
        return False

    matched_list = []
    if not matchmethod:
        matchmethod = match_pymodule
    for fdn in os.listdir(os.path.join(basepath, dirname)):
        try:
            ret_fdn = matchmethod(dirname, fdn)
            if ret_fdn:
                p = os.path.join(dirname, ret_fdn)
                if p not in matched_list:
                    matched_list.append(p)
        except:
            log.debug("Finding module %s failed" % fdn)
            exp2log(log)
    return matched_list

def urlopen(uri, userid='', password=''):
    """
    URLを開いてfile風オブジェクトを返す
    """
    auth = HTTPBasicAuthHandler(HTTPPasswordMgrWithDefaultRealm())
    if userid and password:
        # BASIC認証
        auth.add_password(None, uri, userid, password)
    # openertを作る
    opener = build_opener(auth)
    request = Request(uri)
    request.add_header('User-Agent','procsi 0.1: (feed, process, publish)') 
    r = opener.open(request)
    return r

def make_unique_id(s, prefix=''):
    """
    shelveオブジェクトを受け取り，uniqueなIDを作成して返す
    prefixが渡されたときは，IDの前にprefixをつける
    """
    if not s.has_key('sequence'):
        s['sequence'] = 0
    else:
        s['sequence'] = s['sequence']+1
    s.sync()
    return prefix + str(s['sequence'])

def nifty_formatter(dic, maxlen=0, indent=0, ignorekeys=[]):
    """
    辞書を見やすくフォーマットする
    """
    maxlen = max([len(x) for x in dic.keys()]+[maxlen])
    ret_str = ''
    for k in dic.keys():
        if k not in ignorekeys:
            v = dic[k]
            if isinstance(v, (list, tuple)):
                # リスト，タプルの要素を展開
                v = ', '.join(['%s' % x for x in v])
            if isinstance(v, dict):
                # 辞書の要素をインデントしつつ展開
                v = nifty_formatter(v, indent=indent+maxlen)
            ret_str += """%s%s : %s\n""" % (' '*indent, k.ljust(maxlen), v)
    return ret_str

def nifty_input(keys):
    """
    複数の要素を標準入力などから入力し，辞書を返す
    """
    print "Input vaues:"
    total = len(keys)
    ret_d = {}
    for c, k in enumerate(keys):
        ret_d[k] = raw_input("""%s(%d/%d) : """ % (k, c+1, total))
    return ret_d

def dry_feed(feeder_id, feedmanager):
    """
    Feederのテストをする，結果を標準出力に出力する

    feeder_id   : FeederのID
    feedmanager : プラグイン読み込み済みのFeedManagerインスタンス
    """
    l = feedmanager.feed(feeder_id)
    print """### Dry run - feeder %s ###""" % feeder_id
    for c, item in enumerate(l):
        print """----- Feeder output %d ----""" % c
        print nifty_formatter(item, ignorekeys=['channel'])

def dry_publish(publisher_id, publishermanager):
    """
    Publisherのテストをする，publishまたはtest_publishを呼ぶ

    publisher_id     : PublisherのID
    publishermanager : プラグイン読み込み済みのPublisherManagerインスタンス
    """
    item = nifty_input(('title', 'link', 'summary'))
    item['updated'] = datetime.now()
    print """### Dry run - publisher %s ###""" % publisher_id
    print publishermanager.test_publish(publisher_id, item)


def get_var_path(basedir=os.path.dirname(os.path.realpath(__file__))):
    """
    varのパスを返す
    """
    return os.path.join(basedir, 'var')


